﻿//  -------------------- iSharePoint -------------------- //
// 
//  Company:	IT Complex, LLC
//  Project:	Smart.Common
//  File name:	NotificationContext.cs
//  Developer:	Solomatov Igor
//  Created:	15.04.2012
// 
//  -------------------- iSharePoint -------------------- //

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Workflow;
using Smart.Common.Entities;
using Smart.Common.Templates;

namespace Smart.Common.Notifications
{
	public class NotificationScope : WebEntityScope, INotificationScope
	{
		/// <summary>
		/// Notification templates
		/// </summary>
		public IEntityFacade<NotificationTemplate> Templates
		{
			get { return Get<NotificationTemplate>(Const.FacadeNames.MessageTemplates); }
		}

		/// <summary>
		/// Notification queue
		/// </summary>
		public IEntityFacade<NotificationItem> Queue
		{
			get { return Get<NotificationItem>(Const.FacadeNames.MessageQueue); }
		}

		/// <summary>
		/// Return default NotificationContext settings
		/// </summary>
		protected override string GetDefaultSettings(string scopeTypeName, string scopeName)
		{
			return
				GetDefaultSettingsHeader(scopeTypeName, "/") + "\r\n" +
				Const.FacadeNames.MessageQueue + "#Lists/MQ\r\n" +
				Const.FacadeNames.MessageTemplates + "#Lists/MT#false#1:0:0#0:15:0#BelowNormal";
		}

		/// <summary>
		/// Returns template service
		/// </summary>
		protected virtual ITemplateService GetService()
		{
			return SPServices.TemplateFactory.GetService(Const.Templates.Html);
		}

		/// <summary>
		/// Format notification text
		/// </summary>
		protected virtual string FormatText(ITemplateService service, string text, IDictionary<string, object> values)
		{
			return (string)service.FormatData((object)text, values);
		}

		/// <summary>
		/// Format notification text
		/// </summary>
		protected virtual void FormatText(string subjectTemplate, string bodyTemplate, IDictionary<string, object> values, out string subject, out string body)
		{
			var service = GetService();

			subject = FormatText(service, subjectTemplate, values);
			body = FormatText(service, bodyTemplate, values);
		}

		/// <summary>
		/// Load template by <paramref name="templateId"/> (item title)
		/// </summary>
		public virtual NotificationTemplate GetTemplate(string templateId)
		{
			return
				Templates.Query.Where.Eq(SN.TitleField, templateId).Executor.ExecuteOne();
		}

		/// <summary>
		/// Queue notification message formatting template identified by <paramref name="templateId"/>
		/// </summary>
		/// <returns>true, if succeeded</returns>
		public virtual bool QueueItem(string templateId, string emails, IDictionary<string, object> values)
		{
			var template = GetTemplate(templateId);

			if (template == null)
			{
				H.LT("NotificationContext.QueueItem.Exception: Template '{0}' for '{1}' not found, notification was not sent!", templateId, emails);
				return false;
			}

			string subject;
			string body;

			FormatText(template.Subject, template.Body, values, out subject, out body);

			return
				QueueItem(subject, body, emails, DateTime.Now);
		}

		/// <summary>
		/// Queue notification message formatting template
		/// </summary>
		/// <returns>true, if succeeded</returns>
		public virtual bool QueueItem(string subjectTemplate, string bodyTemplate, string emails, IDictionary<string, object> values)
		{
			if (emails.IsNullOrEmpty())
			{
				H.LT("NotificationContext.QueueItem.Exception: 'emails' is empty, notification '{0}' was not sent!", subjectTemplate);
				return false;
			}

			string subject;
			string body;

			FormatText(subjectTemplate, bodyTemplate, values, out subject, out body);

			return
				QueueItem(subject, body, emails, DateTime.Now);
		}

		/// <summary>
		/// Queue notification without formatting
		/// </summary>
		/// <returns>true, if succeeded</returns>
		public virtual bool QueueItem(string subject, string body, string emails, DateTime sendAt)
		{
			emails = ValidateEmails(emails);

			if (subject.IsNullOrEmpty() || emails.IsNullOrEmpty())
			{
				H.LT("NotificationContext.QueueItem.Exception: 'emails' or 'subject' is empty, notification '{0}' for '{1}' was not sent!", subject, emails);
				return false;
			}

			var instance = Queue.Create();

			if (instance == null)
				return false;

			instance.Address = emails;
			instance.Subject = subject;
			instance.Body = body;
			instance.SendAt = sendAt;
			instance.RetriesNo = 0;

			return instance.Save();
		}

		/// <summary>
		/// Return values collection for <paramref name="item"/>
		/// </summary>
		public virtual Dictionary<string, object> GetValues(SPListItem item)
		{
			return
				GetValues(item, null, null);
		}

		/// <summary>
		/// Return values collection for <paramref name="item"/>, <paramref name="taskItem"/> and <paramref name="taskProperties"/>
		/// </summary>
		public virtual Dictionary<string, object> GetValues(SPListItem item, SPListItem taskItem, SPWorkflowTaskProperties taskProperties)
		{
			var values = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);

			try
			{
				var web =
					item != null
						? item.Web
						: taskItem != null
							? taskItem.Web
							: SPH.GetWeb();

				taskProperties.GetTaskValues(web, item, taskItem, null, values);
			}
			catch (Exception x)
			{
				H.LX("NotificationContext.GetValues", x);
			}

			return values;
		}

		/// <summary>
		/// Returns comma or semicolon separated emails for <paramref name="principal"/>
		/// </summary>
		public virtual string GetEmails(string principal)
		{
			return Web.GetEmails(principal);
		}

		/// <summary>
		/// Returns comma or semicolon separated emails for <paramref name="principalId"/>
		/// </summary>
		public virtual string GetEmails(int principalId)
		{
			return Web.GetEmails(principalId);
		}

		/// <summary>
		/// Returns comma or semicolon separated emails for <paramref name="principal"/>
		/// </summary>
		public virtual string GetEmails(SPPrincipal principal)
		{
			return principal.GetEmails();
		}

		/// <summary>
		/// Validate email
		/// </summary>
		/// <returns>true, if valid</returns>
		public virtual bool IsValidEmail(string email)
		{
			var valid = Const.RegexEmail.IsMatch(email);

			if (!valid)
				H.LT("NotificationContext.IsValidEmail.NotValidException: '{0}'!", email);

			return valid;
		}

		/// <summary>
		/// Validate comma or semicolon separated emails
		/// </summary>
		/// <returns>Valid comma or semicolon separated emails</returns>
		public virtual string ValidateEmails(string emails)
		{
			return
				emails
					.SplitByCommaOrSemicolon()
					.Where(IsValidEmail)
					.CastToString(";");
		}
	}
}